import { is_array,is_object } from "./util";

export default class Action {
    static i () {
        if (false === this.instance instanceof this) {
            this.instance = new this
        }
        return this.instance
    }
    constructor () {
        this.okays = {}
        if (this.__initial__) {
            this.__initial__()
        }
        // 变量监听
        this.proxy(this, '')
    }
    proxy (obj, path) {
        let that = this
        Object.keys(obj).forEach((key) =>{
            if(key == 'okays' || key == 'deny' || (this.deny && this.deny.indexOf(key) != -1)) {
                return
            }
            let val = obj[key]
            let path0 = (path == '' || path == undefined)?key:(path + '.' + key);''
            if(is_object(val)){
                Object.defineProperty(obj, key, {
                    get: function () {
                        return val
                    },
                    set: function (value) {
                        val = value;
                        if(is_object(val) || is_array(val)){
                            that.proxy(val,path0)
                        }
                        for(let key in that.okays) {
                            if(Object.prototype.toString.call(this.okays[key]) == '[object Set]' ) {
                                this.okays[key].forEach((k,v)=> {
                                    v.actions_publish(that, path0)
                                })
                            }else {
                                that.okays[key].actions_publish(that, path0)
                            }
                        }
                    }
                })
                that.proxy(val, path0)
            }else if(is_array(val)){
                let arrayProto = Array.prototype
                let arrayMethods = Object.create(arrayProto)
                let newArrProto = [];
                [
                    'push',
                    'pop',
                    'shift',
                    'unshift',
                    'splice',
                    'sort',
                    'reverse'
                ].forEach(function(method){
                    let original = arrayMethods[method]
                    newArrProto[method] = function mutator() {
                        original.apply(this, arguments)
                        if (method == 'push') {
                            that.proxy(arguments[0],  path0 + '.' + (this.length - 1))
                        } else if(method == 'unshift') {
                            for(let i = arguments.length - 1; i >=0;i--) {
                                that.proxy(arguments[i], path0 + '.' + i)
                            }
                        }
                        let __post = {method:method,arguments:arguments}
                        for(let key in that.okays) {
                            if(Object.prototype.toString.call(this.okays[key]) == '[object Set]' ) {
                                this.okays[key].forEach((k,v)=> {
                                    v.actions_publish(that, path0, __post)
                                    v.actions_publish(that, path0 + '.length', __post)
                                })
                            }else {
                                that.okays[key].actions_publish(that, path0, __post)
                                that.okays[key].actions_publish(that, path0 + '.length', __post)
                            }

                        }
                    }
                })
                val.__proto__  = newArrProto
                that.proxy(val, path0)
            }else{
                Object.defineProperty(obj, key, {
                    get: function (){
                        return val;
                    },
                    set: function (value) {
                        val = value;
                        if(key != 'proxy' && key != 'proxyed'){
                            for(let key in that.okays) {
                                if(Object.prototype.toString.call(this.okays[key]) == '[object Set]' ) {
                                    this.okays[key].forEach((k,v)=> {
                                        v.actions_publish(that, path0)
                                    })
                                }else {
                                    that.okays[key].actions_publish(that, path0)
                                }

                            }
                        }
                    }
                })
            }
        })
    }
    addOkay (okay) {
        if(okay.parent != null && okay.parent != undefined) {
            if(okay.parent.tagName == okay.parent.inst) {
                if (this.okays[okay.parent.tagName]) {
                    if (Object.prototype.toString.call(this.okays[okay.parent.tagName]) == '[object Set]') {
                        this.okays[okay.parent.tagName].add(okay)
                    }else {
                        let set = new Set()
                        set.add(this.okays[okay.parent.tagName])
                        this.okays[okay.parent.tagName] = set
                        this.okays[okay.parent.tagName].add(okay)
                    }
                }else {
                    this.okays[okay.parent.tagName] = okay
                }
            }else {
                this.okays[okay.parent.inst] = okay
            }
        }else {
            if(!this.okays.__default__) {
                this.okays.__default__ = new Set()
            }
            this.okays.__default__.add(okay)
        }

    }
    removeOkay(okay) {
        this.okays.delete(okay)
    }

}
Action.EOA = 'END_OF_ACTION'
